import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, filedialog
import importlib
import threading
import os
import pandas as pd
import json
import re
from datetime import datetime

class ApiTesterGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("API接口测试工具")
        self.root.geometry("900x700")
        
        # 设置中文字体
        self.style = ttk.Style()
        self.style.configure("Treeview", font=('SimHei', 10))
        self.style.configure("Treeview.Heading", font=('SimHei', 10, 'bold'))
        
        self.setup_ui()
        self.load_api_list()
    
    def setup_ui(self):
        # 左侧API列表
        left_frame = ttk.LabelFrame(self.root, text="API列表", padding=(5, 5, 5, 5))
        left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=False, padx=5, pady=5, ipadx=5, ipady=5)
        
        # API类别标签
        category_label = ttk.Label(left_frame, text="选择接口类别:", font=('SimHei', 10))
        category_label.pack(anchor=tk.W, pady=(0, 5))
        
        # API类别选择
        self.category_var = tk.StringVar()
        self.category_combo = ttk.Combobox(left_frame, textvariable=self.category_var, state="readonly", width=25)
        self.category_combo['values'] = ("用户相关", "应用相关", "聊天历史相关")
        self.category_combo.current(0)
        self.category_combo.pack(anchor=tk.W, pady=(0, 10))
        self.category_combo.bind("<<ComboboxSelected>>", self.on_category_change)
        
        # API列表
        self.api_tree = ttk.Treeview(left_frame, columns=("api_name"), show="tree", height=25, selectmode="browse")
        self.api_tree.heading("#0", text="API名称")
        self.api_tree.pack(fill=tk.BOTH, expand=True)
        self.api_tree.bind("<<TreeviewSelect>>", self.on_api_select)
        
        # 右侧操作区域
        right_frame = ttk.LabelFrame(self.root, text="操作区域", padding=(5, 5, 5, 5))
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5, ipadx=5, ipady=5)
        
        # 参数设置区域
        params_frame = ttk.LabelFrame(right_frame, text="参数设置", padding=(5, 5, 5, 5))
        params_frame.pack(fill=tk.BOTH, expand=False, pady=(0, 10))
        
        # 参数滚动区域
        self.params_canvas = tk.Canvas(params_frame)
        scrollbar = ttk.Scrollbar(params_frame, orient="vertical", command=self.params_canvas.yview)
        self.params_scrollable_frame = ttk.Frame(self.params_canvas)
        
        self.params_scrollable_frame.bind(
            "<Configure>",
            lambda e: self.params_canvas.configure(
                scrollregion=self.params_canvas.bbox("all")
            )
        )
        
        self.params_canvas.create_window((0, 0), window=self.params_scrollable_frame, anchor="nw")
        self.params_canvas.configure(yscrollcommand=scrollbar.set)
        
        self.params_canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
        
        # 按钮区域 - 改为两行布局以确保所有按钮都能显示
        button_frame = ttk.Frame(right_frame)
        button_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 第一行 - 复选框
        checkbox_frame = ttk.Frame(button_frame)
        checkbox_frame.pack(fill=tk.X, pady=(0, 5))
        
        # SESSION cookie控制复选框
        self.use_session_cookie_var = tk.BooleanVar(value=True)  # 默认选中
        self.session_cookie_check = ttk.Checkbutton(
            checkbox_frame, 
            text="使用SESSION Cookie", 
            variable=self.use_session_cookie_var
        )
        self.session_cookie_check.pack(side=tk.LEFT, padx=5)
        
        # Basic认证控制复选框
        self.use_basic_auth_var = tk.BooleanVar(value=True)  # 默认选中
        self.basic_auth_check = ttk.Checkbutton(
            checkbox_frame, 
            text="使用Basic认证", 
            variable=self.use_basic_auth_var
        )
        self.basic_auth_check.pack(side=tk.LEFT, padx=5)
        
        # 第二行 - 按钮
        action_frame = ttk.Frame(button_frame)
        action_frame.pack(fill=tk.X)
        
        self.run_button = ttk.Button(action_frame, text="运行接口", command=self.run_api)
        self.run_button.pack(side=tk.LEFT, padx=5)
        
        self.clear_button = ttk.Button(action_frame, text="清空结果", command=self.clear_result)
        self.clear_button.pack(side=tk.LEFT, padx=5)
        
        # 自动化测试按钮
        self.auto_test_button = ttk.Button(action_frame, text="运行自动化测试", command=self.run_auto_test)
        self.auto_test_button.pack(side=tk.LEFT, padx=5)
        
        # 结果显示区域
        result_frame = ttk.LabelFrame(right_frame, text="响应结果", padding=(5, 5, 5, 5))
        result_frame.pack(fill=tk.BOTH, expand=True)
        
        # 结果标签页
        self.result_notebook = ttk.Notebook(result_frame)
        self.result_notebook.pack(fill=tk.BOTH, expand=True)
        
        # 单个接口结果标签
        self.single_result_frame = ttk.Frame(self.result_notebook)
        self.result_notebook.add(self.single_result_frame, text="接口结果")
        self.result_text = scrolledtext.ScrolledText(self.single_result_frame, wrap=tk.WORD, font=('SimHei', 10))
        self.result_text.pack(fill=tk.BOTH, expand=True)
        
        # 自动化测试结果标签
        self.auto_result_frame = ttk.Frame(self.result_notebook)
        self.result_notebook.add(self.auto_result_frame, text="自动化测试结果")
        
        # 测试结果表格 - 使用框架包裹Treeview和滚动条
        tree_frame = ttk.Frame(self.auto_result_frame)
        tree_frame.pack(fill=tk.BOTH, expand=True)
        
        columns = ("test_id", "test_name", "status")
        self.test_result_tree = ttk.Treeview(tree_frame, columns=columns, show="headings")
        
        # 设置列宽和标题
        self.test_result_tree.column("test_id", width=120, anchor="center", stretch=True)
        self.test_result_tree.column("test_name", width=150, anchor="center", stretch=True)
        self.test_result_tree.column("status", width=80, anchor="center", stretch=True)
        
        self.test_result_tree.heading("test_id", text="测试用例ID")
        self.test_result_tree.heading("test_name", text="测试名称")
        self.test_result_tree.heading("status", text="测试状态")
        
        # 添加垂直滚动条
        y_scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.test_result_tree.yview)
        self.test_result_tree.configure(yscroll=y_scrollbar.set)
        
        # 添加水平滚动条
        x_scrollbar = ttk.Scrollbar(tree_frame, orient="horizontal", command=self.test_result_tree.xview)
        self.test_result_tree.configure(xscroll=x_scrollbar.set)
        
        # 使用grid布局以确保滚动条正确显示
        self.test_result_tree.grid(row=0, column=0, sticky="nsew")
        y_scrollbar.grid(row=0, column=1, sticky="ns")
        x_scrollbar.grid(row=1, column=0, sticky="ew")
        
        # 配置grid权重，确保Treeview能够随窗口大小调整
        tree_frame.grid_rowconfigure(0, weight=1)
        tree_frame.grid_columnconfigure(0, weight=1)
        
        # 测试统计信息
        self.stats_frame = ttk.LabelFrame(self.auto_result_frame, text="测试统计")
        self.stats_frame.pack(fill=tk.X, pady=5)
        
        self.total_label = ttk.Label(self.stats_frame, text="总用例数: 0")
        self.total_label.pack(side=tk.LEFT, padx=20)
        
        self.pass_label = ttk.Label(self.stats_frame, text="通过数: 0", foreground="green")
        self.pass_label.pack(side=tk.LEFT, padx=20)
        
        self.fail_label = ttk.Label(self.stats_frame, text="失败数: 0", foreground="red")
        self.fail_label.pack(side=tk.LEFT, padx=20)
    
    def load_api_list(self):
        # 清空现有列表
        for item in self.api_tree.get_children():
            self.api_tree.delete(item)
        
        # 根据类别加载API列表
        category = self.category_var.get()
        
        if category == "用户相关":
            apis = {
                "user_add": "创建用户（仅管理员）",
                "user_delete": "删除用户（仅管理员）",
                "user_update": "更新用户（仅管理员）",
                "user_get": "根据ID获取用户（仅管理员）",
                "user_get_vo": "根据ID获取用户信息（脱敏）",
                "user_get_login": "获取当前登录用户",
                "user_login": "用户登录",
                "user_logout": "用户注销",
                "user_register": "用户注册",
                "user_list": "分页获取用户列表（仅管理员）"
            }
        elif category == "应用相关":
            apis = {
                "app_add": "创建应用",
                "app_delete": "删除应用",
                "app_update": "更新应用",
                "app_get_vo": "根据ID获取应用详情",
                "app_deploy": "应用部署",
                "app_download": "下载应用代码",
                "app_my_list": "分页获取当前用户创建的应用列表",
                "app_good_list": "分页获取精选应用列表",
                "app_admin_update": "管理员更新应用",
                "app_admin_delete": "管理员删除应用",
                "app_admin_list": "管理员分页获取应用列表"
            }
        else:  # 聊天历史相关
            apis = {
                "chat_history_app": "分页查询某个应用的对话历史",
                "chat_history_admin_list": "管理员分页查询所有对话历史"
            }
        
        # 添加到树状视图
        for api_key, api_name in apis.items():
            self.api_tree.insert("", "end", iid=api_key, text=api_name)
    
    def on_category_change(self, event):
        self.load_api_list()
        # 清空参数区域和结果区域
        self.clear_params()
        self.clear_result()
    
    def on_api_select(self, event):
        selected_items = self.api_tree.selection()
        if selected_items:
            self.selected_api = selected_items[0]
            self.show_params()
    
    def clear_params(self):
        # 清空参数区域
        for widget in self.params_scrollable_frame.winfo_children():
            widget.destroy()
    
    def show_params(self):
        self.clear_params()
        
        # 根据选择的API显示相应的参数输入框
        api_key = self.selected_api
        param_entries = {}
        
        # 定义每个API需要的参数
        api_params = {
            # 用户相关
            "user_add": ["user_name", "user_account", "user_password", "email", "phone"],
            "user_delete": ["user_id"],
            "user_update": ["user_id", "user_name", "email", "phone"],
            "user_get": ["user_id"],
            "user_get_vo": ["user_id"],
            "user_login": ["user_account", "user_password"],
            "user_register": ["user_account", "user_password", "check_password", "user_name"],
            "user_list": ["page_num", "page_size", "user_id", "user_account"],
            # 应用相关
            "app_add": ["init_prompt"],
            "app_delete": ["app_id"],
            "app_update": ["app_id", "app_name"],
            "app_get_vo": ["app_id"],
            "app_deploy": ["app_id"],
            "app_download": ["app_id", "save_path"],
            "app_my_list": ["page_num", "page_size", "app_id"],
            "app_good_list": ["page_num", "page_size"],
            "app_admin_update": ["app_id", "app_name", "is_good", "is_deleted"],
            "app_admin_delete": ["app_id"],
            "app_admin_list": ["page_num", "page_size", "app_id", "user_id"],
            # 聊天历史相关
            "chat_history_app": ["app_id", "page_size", "last_create_time"],
            "chat_history_admin_list": ["page_num", "page_size", "chat_id", "app_id"]
        }
        
        # 参数中文名称映射
        param_names = {
            "user_id": "用户ID",
            "user_name": "用户名",
            "user_account": "用户账号",
            "user_password": "用户密码",
            "check_password": "确认密码",
            "email": "邮箱（可选）",
            "phone": "电话（可选）",
            "app_id": "应用ID",
            "app_name": "应用名称",
            "init_prompt": "初始化提示词",
            "save_path": "保存路径（可选）",
            "page_num": "页码",
            "page_size": "每页大小",
            "last_create_time": "最后创建时间（可选）",
            "chat_id": "对话ID（可选）",
            "is_good": "是否精选（可选）",
            "is_deleted": "是否删除（可选）"
        }
        
        # 默认值
        default_values = {
            "page_num": "1",
            "page_size": "10"
        }
        
        if api_key in api_params:
            params = api_params[api_key]
            
            for param in params:
                row_frame = ttk.Frame(self.params_scrollable_frame)
                row_frame.pack(fill=tk.X, pady=2)
                
                label = ttk.Label(row_frame, text=f"{param_names.get(param, param)}:", width=20)
                label.pack(side=tk.LEFT, padx=5)
                
                entry = ttk.Entry(row_frame)
                entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
                
                # 设置默认值
                if param in default_values:
                    entry.insert(0, default_values[param])
                
                param_entries[param] = entry
        
        # 保存参数输入框引用
        self.param_entries = param_entries
    
    def clear_result(self):
        self.result_text.delete(1.0, tk.END)
    
    def run_api(self):
        if not hasattr(self, 'selected_api'):
            messagebox.showwarning("警告", "请先选择一个API")
            return
        
        # 禁用运行按钮
        self.run_button.config(state=tk.DISABLED)
        
        # 清空结果区域
        self.clear_result()
        self.result_text.insert(tk.END, "正在执行API请求，请稍候...\n")
        
        # 在新线程中运行API
        threading.Thread(target=self._run_api_thread).start()
    
    def _run_api_thread(self):
        try:
            api_key = self.selected_api
            
            # 获取参数
            params = {}
            for param_name, entry in self.param_entries.items():
                value = entry.get().strip()
                if value:  # 只添加非空参数
                    # 处理布尔值
                    if param_name in ["is_good", "is_deleted"]:
                        if value.lower() in ["true", "1", "yes"]:
                            params[param_name] = True
                        elif value.lower() in ["false", "0", "no"]:
                            params[param_name] = False
                    # 处理数字
                    elif param_name in ["page_num", "page_size", "user_id", "app_id", "chat_id"]:
                        try:
                            params[param_name] = int(value)
                        except ValueError:
                            pass  # 保留字符串值
                    else:
                        params[param_name] = value
            
            # 动态导入对应的API模块
            module_name = f"api_tests.{api_key}"
            module = importlib.import_module(module_name)
            
            # 获取对应的类名（首字母大写转换）
            class_name = ''.join(word.capitalize() for word in api_key.split('_')) + 'Api'
            api_class = getattr(module, class_name)
            
            # 创建实例
            api_instance = api_class()
            
            # 设置SESSION cookie使用状态
            if hasattr(api_instance, 'set_session_cookie_status'):
                api_instance.set_session_cookie_status(self.use_session_cookie_var.get())
                
            # 设置Basic认证使用状态
            if hasattr(api_instance, 'set_basic_auth_status'):
                api_instance.set_basic_auth_status(self.use_basic_auth_var.get())
            
            # 调用对应的方法
            method_name = api_key.replace('_', '_')
            if api_key in ["user_get_login", "user_logout"]:
                # 这些方法不需要参数
                result = api_instance.__getattribute__(api_key)()
            else:
                # 其他方法需要参数
                # 对于包含多个下划线的方法名，需要特殊处理
                method_map = {
                    "user_get": "get_user",
                    "user_get_vo": "get_user_vo",
                    "user_add": "add_user",
                    "user_delete": "delete_user",
                    "user_update": "update_user",
                    "user_login": "login",
                    "user_register": "register_user",
                    "user_list": "get_user_list",
                    "app_add": "add_app",
                    "app_delete": "delete_app",
                    "app_update": "update_app",
                    "app_get_vo": "get_app_vo",
                    "app_deploy": "deploy_app",
                    "app_download": "download_app_code",
                    "app_my_list": "get_my_app_list",
                    "app_good_list": "get_good_app_list",
                    "app_admin_update": "update_app_admin",
                    "app_admin_delete": "delete_app_admin",
                    "app_admin_list": "get_admin_app_list",
                    "chat_history_app": "get_app_chat_history",
                    "chat_history_admin_list": "get_all_chat_history"
                }
                
                method_name = method_map.get(api_key, api_key)
                result = api_instance.__getattribute__(method_name)(**params)
            
            # 更新结果到UI（在主线程中）
            self.root.after(0, lambda: self._update_result(result))
        
        except Exception as e:
            # 显示错误信息
            error_msg = f"执行出错：{str(e)}"
            self.root.after(0, lambda: self._update_result(error_msg))
        
        finally:
            # 启用运行按钮
            self.root.after(0, lambda: self.run_button.config(state=tk.NORMAL))
    
    def _update_result(self, result):
        self.clear_result()
        
        # 检查是否是登录API的结果
        result_str = str(result)
        
        # 添加登录成功信息和SESSION Cookie提示
        if hasattr(self, 'selected_api') and self.selected_api == 'user_login':
            # 检查是否登录成功（根据返回的结果判断）
            if '成功' in result_str or '200' in result_str:
                # 显示登录成功信息
                login_success_msg = "登录成功！\n"
                # 添加SESSION Cookie使用提示
                if self.use_session_cookie_var.get():
                    login_success_msg += "已启用SESSION Cookie，将在后续请求中自动使用。\n\n"
                else:
                    login_success_msg += "注意：SESSION Cookie未启用，后续请求可能需要重新登录。\n\n"
                self.result_text.insert(tk.END, login_success_msg)
            elif '40100' in result_str or '未登录' in result_str:
                # 显示未登录提示
                self.result_text.insert(tk.END, "未登录或登录失效，请先登录。\n\n")
        
        # 显示原始结果
        self.result_text.insert(tk.END, result_str)
    
    def run_auto_test(self):
        """运行自动化测试"""
        # 选择测试用例文件或目录
        test_dir = filedialog.askdirectory(title="选择测试用例目录", initialdir=r"d:\swu\python\ceshi\doubao\测试用例")
        if not test_dir:
            return
        
        # 获取所有Excel文件
        excel_files = [f for f in os.listdir(test_dir) if f.endswith('.xlsx')]
        if not excel_files:
            messagebox.showwarning("警告", "选择的目录中没有找到Excel测试用例文件")
            return
        
        # 清空测试结果表格
        for item in self.test_result_tree.get_children():
            self.test_result_tree.delete(item)
        
        # 禁用按钮
        self.auto_test_button.config(state=tk.DISABLED)
        
        # 切换到测试结果标签页
        self.result_notebook.select(self.auto_result_frame)
        
        # 在新线程中运行自动化测试
        threading.Thread(target=self._run_auto_test_thread, args=(test_dir, excel_files)).start()
    
    def _run_auto_test_thread(self, test_dir, excel_files):
        """自动化测试线程"""
        total_tests = 0
        passed_tests = 0
        failed_tests = 0
        # 收集所有测试用例的输入和输出信息
        all_test_data = []
        
        try:
            for excel_file in excel_files:
                # 只处理原始xlsx文件，跳过_updated文件
                if '_updated' in excel_file:
                    continue
                
                file_path = os.path.join(test_dir, excel_file)
                self.root.after(0, lambda file=excel_file: self._update_auto_test_status(f"正在处理文件: {file}..."))
                
                # 读取Excel文件
                df = pd.read_excel(file_path)
                
                # 解析测试用例
                test_cases, df, last_column, use_last_column_as_expected = self._parse_test_cases(df)
                
                # 创建一个副本
                updated_df = df.copy()
                
                # 检查"输出结果"列是否已存在，如果不存在则添加
                output_column = '输出结果'
                if output_column not in updated_df.columns:
                    # 在最后一列后添加"输出结果"列，保留原始的最后一列（优先级列）
                    updated_df.insert(len(updated_df.columns), output_column, '')
                
                # 执行测试用例
                for test_case in test_cases:
                    # 将文件名添加到测试用例中，用于更准确地识别API
                    test_case['file_name'] = excel_file
                    total_tests += 1
                    
                    # 执行测试用例
                    result = self._execute_test_case(test_case)
                    actual_result = result.get('actual_result', '')
                    
                    # 如果有行索引信息，更新Excel数据
                    if 'row_index' in test_case:
                        row_idx = test_case['row_index']
                        # 将实际返回结果填写到"输出结果"列
                        updated_df.at[row_idx, output_column] = actual_result
                    
                    # 检查是否需要比对预期结果
                    test_status = '通过'  # 默认通过
                    if use_last_column_as_expected and 'row_index' in test_case:
                        row_idx = test_case['row_index']
                        expected_result = test_case.get('expected', '')
                        # 简单的结果比对（实际应用中可能需要更复杂的比对逻辑）
                        if actual_result and expected_result:
                            # 如果预期结果是JSON字符串，可以尝试解析后比对
                            try:
                                if isinstance(expected_result, str) and expected_result.strip().startswith('{'):
                                    # 如果是JSON字符串，只比对关键字段
                                    import json
                                    expected_json = json.loads(expected_result)
                                    actual_json = json.loads(actual_result)
                                    # 示例：只比对状态码或关键信息
                                    if 'code' in expected_json and 'code' in actual_json:
                                        if expected_json['code'] != actual_json['code']:
                                            test_status = '失败'
                                else:
                                    # 简单字符串比对
                                    if expected_result not in actual_result:
                                        test_status = '失败'
                            except:
                                # 如果解析失败，进行简单字符串比对
                                if expected_result not in actual_result:
                                    test_status = '失败'
                    
                    # 更新测试状态
                    if test_status == '通过':
                        passed_tests += 1
                    else:
                        failed_tests += 1
                    
                    # 更新结果状态
                    result['status'] = test_status
                    
                    # 更新UI
                    self.root.after(0, lambda tc=test_case, res=result: self._add_test_result(tc, res))
                    
                    # 更新统计信息
                    self.root.after(0, lambda t=total_tests, p=passed_tests, f=failed_tests: self._update_test_stats(t, p, f))
                    
                    # 收集测试用例的输入和输出信息
                    test_data = {
                        '测试用例ID': test_case.get('test_id', ''),
                        '测试名称': test_case.get('test_name', ''),
                        '文件名': test_case.get('file_name', ''),
                        '请求参数': test_case.get('params', ''),
                        '预期响应': test_case.get('expected', ''),
                        '测试结果': result.get('status', ''),
                        '实际响应': result.get('actual_result', ''),
                        '原始响应': str(result.get('raw_result', ''))
                    }
                    all_test_data.append(test_data)
                
                # 保存更新后的Excel文件 - 直接覆盖原始文件
                try:
                    updated_df.to_excel(file_path, index=False)
                    self.root.after(0, lambda file=excel_file: print(f"已更新测试用例文件: {file}"))
                except Exception as save_error:
                    error_msg = f"保存更新后的测试用例文件失败：{str(save_error)}"
                    self.root.after(0, lambda: print(error_msg))
            
            # 保存所有测试用例的输入和输出到新文件
            if all_test_data:
                self._save_test_results(all_test_data)
        
        except Exception as e:
            error_msg = f"自动化测试执行出错：{str(e)}"
            self.root.after(0, lambda: messagebox.showerror("错误", error_msg))
        
        finally:
            # 启用按钮
            self.root.after(0, lambda: self.auto_test_button.config(state=tk.NORMAL))
            self.root.after(0, lambda: messagebox.showinfo("完成", "自动化测试执行完成！"))
    
    def _save_test_results(self, test_data_list):
        """保存测试结果到新文件"""
        try:
            # 创建DataFrame
            df = pd.DataFrame(test_data_list)
            
            # 生成保存路径
            import datetime
            timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            save_dir = os.path.join(os.getcwd(), "测试结果")
            os.makedirs(save_dir, exist_ok=True)
            save_path = os.path.join(save_dir, f"测试结果汇总_{timestamp}.xlsx")
            
            # 保存到Excel文件
            with pd.ExcelWriter(save_path, engine='openpyxl') as writer:
                df.to_excel(writer, index=False, sheet_name='测试结果汇总')
                
                # 调整列宽
                worksheet = writer.sheets['测试结果汇总']
                for column in worksheet.columns:
                    max_length = 0
                    column_letter = column[0].column_letter
                    for cell in column:
                        try:
                            if len(str(cell.value)) > max_length:
                                max_length = len(str(cell.value))
                        except:
                            pass
                    adjusted_width = min(max_length + 2, 50)
                    worksheet.column_dimensions[column_letter].width = adjusted_width
            
            self.root.after(0, lambda: messagebox.showinfo("保存成功", f"测试结果已保存到：{save_path}"))
            
        except Exception as e:
            error_msg = f"保存测试结果失败：{str(e)}"
            self.root.after(0, lambda: messagebox.showerror("错误", error_msg))
    
    def _update_auto_test_status(self, status):
        """更新自动化测试状态"""
        # 简单的状态显示，可以根据需要扩展
        print(status)
    
    def _parse_test_cases(self, df):
        """解析Excel测试用例，检查最后一列是否为预期返回结果"""
        test_cases = []
        current_case = None
        
        # 检查最后一列
        last_column = df.columns[-1] if len(df.columns) > 0 else None
        use_last_column_as_expected = False
        
        # 无论最后一列是什么，都将其视为可能包含预期结果的列（优先级列）
        # 我们不修改最后一列，而是在其后添加输出结果列
        if last_column:
            # 标记使用最后一列作为预期结果（优先级）进行比对
            use_last_column_as_expected = True
            
        for index, row in df.iterrows():
            # 如果有测试用例ID，则开始新的测试用例
            if pd.notna(row.get('测试用例 ID')):
                if current_case:
                    test_cases.append(current_case)
                
                # 基本测试用例信息
                current_case = {
                    'test_id': str(row.get('测试用例 ID', '')).strip(),
                    'test_name': str(row.get('测试名称', '')).strip(),
                    'headers': str(row.get('请求头', '')).strip(),
                    'params': str(row.get('请求参数（JSON 示例）', '')).strip(),
                    'expected': str(row.get('预期响应', '')).strip(),
                    'test_type': str(row.get('测试类型', '')).strip(),
                    'priority': str(row.get('优先级', '')).strip()
                }
                
                # 如果需要使用最后一列作为预期结果
                if use_last_column_as_expected and pd.notna(row.get(last_column)):
                    current_case['expected'] = str(row.get(last_column)).strip()
                
                # 保存行索引信息，以便后续可以更新该测试用例
                current_case['row_index'] = index
            # 否则补充当前测试用例的信息
            elif current_case:
                for col in df.columns:
                    if pd.notna(row.get(col)):
                        current_case[col] = current_case.get(col, '') + ' ' + str(row.get(col)).strip()
        
        # 添加最后一个测试用例
        if current_case:
            test_cases.append(current_case)
        
        # 将DataFrame和相关信息添加到测试用例列表中，以便后续可能的修改
        return test_cases, df, last_column, use_last_column_as_expected
    
    def _execute_test_case(self, test_case):
        """执行单个测试用例"""
        try:
            # 根据测试用例名称和文件名确定对应的API
            api_key = self._get_api_key_from_test_name(test_case['test_name'], test_case.get('file_name', ''))
            if not api_key:
                return {
                    'status': '失败',
                    'actual_result': '无法识别的API名称',
                    'error': '测试用例名称与系统中的API不匹配'
                }
            
            # 解析参数，传入api_key以便正确处理分页参数
            params = self._parse_params(test_case['params'], api_key)
            
            # 动态导入对应的API模块
            module_name = f"api_tests.{api_key}"
            module = importlib.import_module(module_name)
            
            # 获取对应的类名
            class_name = ''.join(word.capitalize() for word in api_key.split('_')) + 'Api'
            api_class = getattr(module, class_name)
            
            # 创建实例
            api_instance = api_class()
            
            # 设置认证状态
            if hasattr(api_instance, 'set_session_cookie_status'):
                api_instance.set_session_cookie_status(self.use_session_cookie_var.get())
            if hasattr(api_instance, 'set_basic_auth_status'):
                api_instance.set_basic_auth_status(self.use_basic_auth_var.get())
            
            # 调用对应的方法
            method_map = {
                "user_get": "get_user",
                "user_get_vo": "get_user_vo",
                "user_add": "add_user",
                "user_delete": "delete_user",
                "user_update": "update_user",
                "user_login": "login",
                "user_register": "register_user",
                "user_list": "get_user_list",
                "app_add": "add_app",
                "app_delete": "delete_app",
                "app_update": "update_app",
                "app_get_vo": "get_app_vo",
                "app_deploy": "deploy_app",
                "app_download": "download_app_code",
                "app_my_list": "get_my_app_list",
                "app_good_list": "get_good_app_list",
                "app_admin_update": "update_app_admin",
                "app_admin_delete": "delete_app_admin",
                "app_admin_list": "get_admin_app_list",
                "chat_history_app": "get_app_chat_history",
                "chat_history_admin_list": "get_all_chat_history"
            }
            
            if api_key in ["user_get_login", "user_logout"]:
                result = api_instance.__getattribute__(api_key)()
            else:
                method_name = method_map.get(api_key, api_key)
                result = api_instance.__getattribute__(method_name)(**params)
            
            # 验证结果
            is_passed = self._verify_result(result, test_case['expected'])
            
            return {
                'status': '通过' if is_passed else '失败',
                'actual_result': str(result)[:100] + '...' if len(str(result)) > 100 else str(result),
                'raw_result': result
            }
        
        except Exception as e:
            return {
                'status': '失败',
                'actual_result': f"执行错误: {str(e)}",
                'error': str(e)
            }
    
    def _get_api_key_from_test_name(self, test_name, file_name=""):
        """根据测试名称和文件名确定API key"""
        # 1. 首先尝试根据文件名匹配API（更准确）
        file_api_mapping = {
            '下载应用代码': 'app_download',
            '分页查询某个应用的对话历史': 'chat_history_app',
            '分页获取当前用户创建的应用列表': 'app_my_list',
            '分页获取用户列表（仅管理员）': 'user_list',
            '分页获取精选应用列表': 'app_good_list',
            '创建应用': 'app_add',
            '创建用户（仅管理员）': 'user_add',
            '删除应用': 'app_delete',
            '删除用户（仅管理员）': 'user_delete',
            '应用部署': 'app_deploy',
            '更新应用': 'app_update',
            '更新用户（仅管理员）': 'user_update',
            '根据 id 获取应用详情': 'app_get_vo',
            '根据 id 获取用户信息（脱敏）': 'user_get_vo',
            '根据 id 获取用户（仅管理员）': 'user_get',
            '用户注册接口测试': 'user_register',
            '用户注销接口测试': 'user_logout',
            '用户登录接口': 'user_login',
            '管理员分页查询所有对话历史': 'chat_history_admin_list',
            '管理员分页获取应用列表': 'app_admin_list',
            '管理员删除应用': 'app_admin_delete',
            '管理员更新应用': 'app_admin_update',
            '获取当前登录用户': 'user_get_login'
        }
        
        if file_name:
            for file_keyword, api_key in file_api_mapping.items():
                if file_keyword in file_name:
                    return api_key
        
        # 2. 然后尝试根据测试名称匹配API
        api_mapping = {
            '创建应用': 'app_add',
            '删除应用': 'app_delete',
            '更新应用': 'app_update',
            '获取应用详情': 'app_get_vo',
            '应用部署': 'app_deploy',
            '下载': 'app_download',
            '我的应用列表': 'app_my_list',
            '精选应用列表': 'app_good_list',
            '管理员更新应用': 'app_admin_update',
            '管理员删除应用': 'app_admin_delete',
            '管理员获取应用列表': 'app_admin_list',
            '创建用户': 'user_add',
            '删除用户': 'user_delete',
            '更新用户': 'user_update',
            '获取用户': 'user_get',
            '获取用户信息': 'user_get_vo',
            '获取当前登录用户': 'user_get_login',
            '用户登录': 'user_login',
            '用户注销': 'user_logout',
            '用户注册': 'user_register',
            '用户列表': 'user_list',
            '对话历史': 'chat_history_app',
            '所有对话历史': 'chat_history_admin_list'
        }
        
        # 先尝试精确匹配
        for keyword, api_key in api_mapping.items():
            if keyword in test_name:
                return api_key
        
        # 3. 最后尝试根据测试ID前缀匹配
        id_prefix_mapping = {
            'TC-APP': 'app',
            'TC-CHAT': 'chat',
            'TC-USER': 'user'
        }
        
        for prefix, category in id_prefix_mapping.items():
            if test_name.startswith(prefix):
                # 根据测试名称中的关键词进一步细化
                if '下载' in test_name:
                    return 'app_download'
                elif '对话' in test_name or '聊天' in test_name:
                    return 'chat_history_app'
                elif '用户列表' in test_name:
                    return 'user_list'
                elif '创建' in test_name:
                    return f'{category}_add'
                elif '删除' in test_name:
                    return f'{category}_delete'
                elif '更新' in test_name:
                    return f'{category}_update'
                elif '获取' in test_name or '查询' in test_name:
                    if '详情' in test_name or '信息' in test_name:
                        return f'{category}_get_vo'
                    else:
                        return f'{category}_get'
                elif '登录' in test_name:
                    return 'user_login'
                elif '注册' in test_name:
                    return 'user_register'
                elif '注销' in test_name:
                    return 'user_logout'
        
        print(f"无法识别的测试用例: {test_name}, 文件名: {file_name}")
        return None
    
    def _parse_params(self, params_str, api_key=None):
        """解析参数字符串"""
        params = {}
        
        # 尝试从JSON字符串中提取参数
        try:
            # 移除可能的说明文字，只保留JSON部分
            json_match = re.search(r'\{[^}]+\}', params_str)
            if json_match:
                json_str = json_match.group(0)
                # 替换中文引号为英文引号
                json_str = json_str.replace('"', '"').replace('"', '"')
                params = json.loads(json_str)
        except:
            # 如果不是有效的JSON，尝试简单的键值对解析
            try:
                # 从示例中提取参数名和默认值
                if 'initPrompt' in params_str:
                    params['init_prompt'] = '测试提示词'
                elif 'appId' in params_str or 'app_id' in params_str.lower():
                    params['app_id'] = 1
                elif 'userId' in params_str or 'user_id' in params_str.lower():
                    params['user_id'] = 1
            except:
                pass
        
        # 参数名转换：驼峰命名转为下划线命名
        # 处理登录相关参数
        if 'userAccount' in params:
            params['user_account'] = params.pop('userAccount')
        if 'userPassword' in params:
            params['user_password'] = params.pop('userPassword')
        # 处理应用创建参数
        if 'initPrompt' in params:
            params['init_prompt'] = params.pop('initPrompt')
        # 处理其他可能的ID参数
        if 'appId' in params:
            params['app_id'] = params.pop('appId')
        if 'userId' in params:
            params['user_id'] = params.pop('userId')
        
        # 设置默认分页参数 - 只为需要分页的API添加
        # 判断是否为分页相关的API
        pagination_apis = ['user_list', 'app_my_list', 'app_good_list', 'app_admin_list', 
                          'chat_history_app', 'chat_history_admin_list']
        if api_key in pagination_apis:
            if 'page_num' not in params:
                params['page_num'] = 1
            if 'page_size' not in params:
                params['page_size'] = 10
        
        return params
    
    def _verify_result(self, actual_result, expected_result):
        """验证测试结果"""
        # 简单的结果验证逻辑
        actual_str = str(actual_result).lower()
        expected_str = expected_result.lower()
        
        # 检查状态码
        if '200' in expected_str and '200' in actual_str:
            return True
        
        # 检查错误码
        if '40100' in expected_str and '40100' in actual_str:
            return True
        
        # 检查关键词
        if '成功' in expected_str and '成功' in actual_str:
            return True
        if '失败' in expected_str and '失败' in actual_str:
            return True
        
        # 如果预期包含特定状态码，检查实际结果是否包含
        status_codes = re.findall(r'状态码：(\d+)', expected_str)
        for code in status_codes:
            if code in actual_str:
                return True
        
        return False
    
    def _add_test_result(self, test_case, result):
        """添加测试结果到表格"""
        # 设置行颜色
        tags = ('passed',) if result['status'] == '通过' else ('failed',)
        
        # 确保数据存在，避免空值问题
        actual_result = str(result.get('actual_result', '无'))
        expected_result = str(test_case.get('expected', '无'))
        
        # 添加数据
        self.test_result_tree.insert(
            '', 
            'end', 
            values=(
                test_case.get('test_id', '未知'),
                test_case.get('test_name', '未知'),
                result.get('status', '未知')
            ),
            tags=tags
        )
        
        # 设置标签样式
        self.test_result_tree.tag_configure('passed', foreground='green')
        self.test_result_tree.tag_configure('failed', foreground='red')
        
        # 确保表格正确刷新
        self.test_result_tree.update_idletasks()
    
    def _update_test_stats(self, total, passed, failed):
        """更新测试统计信息"""
        self.total_label.config(text=f"总用例数: {total}")
        self.pass_label.config(text=f"通过数: {passed}")
        self.fail_label.config(text=f"失败数: {failed}")

if __name__ == "__main__":
    # 确保程序启动时只显示GUI，不自动执行测试
    import sys
    print(f"GUI模式启动，PID: {os.getpid()}")
    
    # 设置中文字体支持
    root = tk.Tk()
    app = ApiTesterGUI(root)
    root.mainloop()